[ORG 500h]

%include "oskernel/boot/include/common.inc"

KERNEL_ADDR equ 0x1200

[SECTION .gdt]
SEG_BASE equ 0
SEG_LIMIT equ 0xfffff

ARDS_TIMES_BUFFER equ 0x7e00
ARDS_BUFFER equ 0x7e02
ARDS_TIMES dw 0
CHECK_BUFFER_OFFSET dw 0

CODE_SELECTOR equ 0x08
DATA_SELECTOR equ 0x10

gdt_base:
    dd 0
    dd 0
gdt_code:
    dw SEG_LIMIT & 0xffff
    dw SEG_BASE & 0xffff
    db (SEG_BASE >> 16) & 0xff
    ;    P_DPL_S_TYPE
    db 0b1_00_1_1000
    db 0b0_1_00_0000 | ((SEG_LIMIT >> 16) & 0xf)
    db SEG_BASE >> 24 & 0xff
gdt_data:
    dw SEG_LIMIT & 0xffff
    dw SEG_BASE & 0xffff
    db (SEG_BASE >> 16) & 0xff
    ;    P_DPL_S_TYPE
    db 0b1_00_1_0010
    ; G_DB_AVL_LIMT
    db 0b1_1_00_0000 | ((SEG_LIMIT >> 16) & 0xf)
    db SEG_BASE >> 24 & 0xff

gdt_ptr:
    dw $ - gdt_base
    dd gdt_base

[SECTION .text]
[BITS 16]
global setup_start
setup_start:
    mov ax, 0
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, prepare_enter_protected_mode_msg
    call print

    call memory_check

enter_protected_mode:
    cli

    lgdt [gdt_ptr]

    in al, 92h
    ; 2 = 0b00000010
    or al, 2
    out 92h, al

    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp CODE_SELECTOR:protected_mode


memory_check:
    xor ebx, ebx
    mov di, ARDS_BUFFER
.loop:
    mov eax, 0xe820
    mov ecx, 20
    mov edx, 0x534d4150
    int 0x15

    jc .memory_check_error

    add di, cx
    inc dword [ARDS_TIMES]
    cmp ebx, 0
    jne .loop

    mov ax, word [ARDS_TIMES]
    mov word [ARDS_TIMES_BUFFER], ax
    mov [CHECK_BUFFER_OFFSET], di
.memory_check_success:
    mov si, memory_check_success_msg
    call print
    ret

.memory_check_error:
    mov si, memory_check_error_msg
    call print
    jmp $

print:
    mov ah, 0x0e
    mov bh, 0
    mov bl, 1
.loop:
    mov al, [si]
    test al, al
    jz .end
    int 0x10
    inc si
    jmp .loop

.end:
    ret

[BITS 32]
protected_mode:
    mov ax, DATA_SELECTOR
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov esp, 0x9fbff
    mov edi, KERNEL_ADDR
    mov ecx, 3
    mov bl, 30
    call read_hd

    call load_x64_kernel

    jmp CODE_SELECTOR:KERNEL_ADDR


load_x64_kernel:
    xor esi, esi
.loop_load_x64_kernel:
    mov eax, 0x20000
    mul esi
    lea edi, [X64_KERNEL_ADDR_BASE + eax]

    mov eax, 256
    mul esi
    lea ecx, [eax + X64_KERNEL_SECTOR_START]

    mov bl, 0xff
    push esi
    call read_hd
    pop esi

    inc esi
    cmp esi, X64_KERNEL_CONTAIN_SECTORS
    jne .loop_load_x64_kernel

read_hd:
    mov dx, 0x1f2
    mov al, bl
    out dx, al

    mov dx, 0x1f3
    mov al, cl
    out dx, al

    mov dx, 0x1f4
    mov al, ch
    out dx, al

    shr ecx, 16
    mov dx, 0x1f5
    mov al, cl
    out dx, al

    mov dx, 0x1f6
    and ch, 0b1111
    mov al, 0b1110_0000
    or al, ch
    out dx, al

    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

    xor cx, cx
    mov cl, bl
.start_read:
    push cx

    call wait_hd
    call read_hd_data

    pop cx
    loop .start_read

.return:
    ret

wait_hd:
    mov dx, 0x1f7
.wait:
    in al, dx
    and al, 0x88
    cmp al, 0x08
    jnz .wait

    ret

read_hd_data:
    mov dx, 0x1f0
    mov cx, 256
.read_word:
    in ax, dx
    mov [edi], ax
    add edi, 2
    loop .read_word

    ret

prepare_enter_protected_mode_msg:
    db "Prepare to go into protected mode...", 10, 13, 0

memory_check_error_msg:
    db "Memory check error!", 10, 13, 0

memory_check_success_msg:
    db "memory check success!", 10, 13, 0